# -*- coding: utf-8 -*-
"""
@Author: thekernel
@Date: 2020/5/29 08:29
@Description: B站 爬虫
"""
from fake_useragent import UserAgent
import time
import base64
import random

from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium import webdriver

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

from PIL import Image
from selenium.webdriver import ActionChains

import cv2


def is_pixel_equal(image1, image2, x, y):
    """
    判断两张图片 各个位置的像素是否相同
    :param image1:不带缺口的图片
    :param image2: 带缺口的图片
    :param x: 位置x
    :param y: 位置y
    :return: (x,y)位置的像素是否相同
    """
    # 获取两张图片指定位置的像素点
    pixel1 = image1.load()[x, y]
    pixel2 = image2.load()[x, y + 1]
    print("x: ", x, "pixel1: ", pixel1[0], pixel1[1], pixel1[2])
    print("pixel2: ", pixel2[0], pixel2[1], pixel2[2])
    # 设置一个阈值 允许有误差
    threshold = 80
    # 彩色图 每个位置的像素点有三个通道
    if abs(pixel1[0] - pixel2[0]) < threshold or abs(pixel1[1] - pixel2[1]) < threshold or abs(
            pixel1[2] - pixel2[2]) < threshold:
        return True
    else:
        return False


def get_gap(image1, image2):
    """
    获取缺口偏移量
    :param image1:不带缺口的图片
    :param image2: 带缺口的图片
    :return:
    """
    left = 60  # 定义一个左边的起点 缺口一般离图片左侧有一定的距离 有一个滑块
    for i in range(60, image1.size[0]):  # 从左到右 x方向
        for j in range(2, image1.size[1]):  # 从上到下 y方向
            if not is_pixel_equal(image1, image2, i, j):
                left = i  # 找到缺口的左侧边界 在x方向上的位置
                return left
    return left


def get_track(gap):
    """
    根据偏移量 获取移动轨迹
    :param gap: 偏移量
    :return: 移动轨迹
    """

    # 移动轨迹
    track = []
    # 当前位移
    current = 0
    # 减速阈值
    mid = gap * 4 / 5  # 前4/5段加速 后1/5段减速
    # 计算间隔
    t = 0.2
    # 初速度
    v = 0

    while current < gap:
        if current < mid:
            a = 2  # 加速度为+2
        else:
            a = -3  # 加速度为-3

        # 初速度v0
        v0 = v
        # 当前速度
        v = v0 + a * t
        # 移动距离
        move = v0 * t + 1 / 2 * a * t * t
        # 当前位移
        current += move
        # 加入轨迹
        track.append(round(move))

    return track


def get_slider(driver):
    """
    获取滑块
    :return: 滑块对象
    """
    '''
    <div class="geetest_slider_button" style="opacity: 1; transform: translate(0px, 0px);"></div>
    '''
    slider = WebDriverWait(driver, 20).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, '.geetest_slider_button')))

    return slider


def move_to_gap(driver, slider, track):
    """
    拖动滑块到缺口处
    :param slider:滑块
    :param track: 轨迹
    :return:
    """
    ActionChains(driver).click_and_hold(slider).perform()
    for x in track:
        # 只有水平方向有运动 按轨迹移动
        ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform()
    time.sleep(0.5)
    ActionChains(driver).release().perform()  # 松开鼠标


def login_successfully(driver):
    """
    判断是否登陆成功
    :return:
    """
    try:
        # 登录成功后 界面上会有一个消息按钮
        return bool(
            WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, '//a[@title="消息"]')))
        )
    except TimeoutException:
        return False


def screenshot_verify_image(driver):
    """
    1. 截图验证码图片
    2. 获取 canvas 并用 base64 进行解码 转为 bytes 后保存为图片流
    :return:
    """
    im = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.CSS_SELECTOR, '.geetest_canvas_slice')))
    time.sleep(2)
    im.screenshot('./captcha.png')

    canvas_get_script = "return document.getElementsByClassName(\"geetest_canvas_bg geetest_absolute\")[0].toDataURL(\"image/png\");"
    image = driver.execute_script(canvas_get_script)
    image_base64 = image.split(',')[1]
    image_bytes = base64.b64decode(image_base64)
    with open('./verify.png', 'wb') as f:  # 保存图片到本地
        f.write(image_bytes)
    captcha = Image.open("./captcha.png")
    raw_image = cv2.imread("./verify.png")
    resized_image = cv2.resize(raw_image, (captcha.size[0], captcha.size[1]))
    cv2.imwrite("./resized.png", resized_image)


def login(driver):
    """
    登录
    :param driver:
    :return:
    """
    username = "thekernel"
    password = "test"
    login_url = "https://passport.bilibili.com/login"
    driver.get(login_url)
    driver.maximize_window()

    # 输入 用户名、密码并点击登录
    username_input = driver.find_element_by_xpath("//input[@id=\"login-username\"]")
    password_input = driver.find_element_by_xpath("//input[@id=\"login-passwd\"]")
    commit_button = driver.find_element_by_xpath("/html/body/div[1]/div/div[2]/div[3]/div[3]/div/div/div/div[5]/a[1]")
    username_input.send_keys(username)
    password_input.send_keys(password)
    commit_button.click()
    time.sleep(3 + random.random())

    screenshot_verify_image(driver)
    time.sleep(3 + random.random())
    gap = get_gap(Image.open("./captcha.png"), Image.open("./resized.png"))
    print(gap)
    gap += 6

    track = get_track(gap)
    print('滑动轨迹：', track)
    # 点按滑块
    slider = get_slider(driver)
    move_to_gap(driver, slider, track)

    print(login_successfully(driver))


if __name__ == '__main__':
    firefox_driver = "/Users/thekernel/Code/PythonWorkspace/PythonLib/geckodriver"
    options = webdriver.FirefoxOptions()
    # options.add_argument("--headless")
    options.add_argument("disable-infobars")
    # options.add_argument("user-agent=" + UserAgent().random)
    options.add_argument(
        "user-agent=" + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4158.1 Safari/537.36")
    # 掩饰 selenium
    # options.add_experimental_option("excludeSwitches", ["enable-automation"])
    firefox = webdriver.Firefox(options=options, executable_path=firefox_driver)

    login(firefox)
